/**
 * Copyright (c) 2015-2030 by Beijing Yanlight Co., Ltd.
 * All rights reserved.
 */
package com.yanlight.util;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author haolw
 * @time 2017/07/18
 */
public class XmlUtil {

    /**
     * 转换 map to xml
     *
     * @param map
     * @return
     */
    public static String mapToXml(Map<String, String> map) {
        StringBuilder xml = new StringBuilder(16);
        xml.append("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            xml.append(createElement(entry.getKey(), entry.getValue()));
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 转换 map to xml
     *
     * @param map
     * @return
     */
    public static String mapToXmlWithCDATA(Map<String, String> map) {
        StringBuilder xml = new StringBuilder(16);
        xml.append("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            xml.append(createCDATAElement(entry.getKey(), entry.getValue()));
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 转换 map为xml格式数据，非数字用<key></>![CDATA[value]]</key>
     *
     * @param arr
     * @return
     */
    public static String mapToXmlWithParse(Map<String, String> arr) {
        StringBuilder xml = new StringBuilder(16);
        xml.append("<xml>");
        for (Map.Entry<String, String> entry : arr.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            if (isNumeric(val))
                xml.append(createElement(key, val));
            else {
                xml.append(createCDATAElement(key, val));
            }
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * xml 转换为 字典序 map
     *
     * @param xml
     * @return
     */
    public static SortedMap<String, String> XmlToSortedMap(String xml) {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        SortedMap<String, String> map = new TreeMap<String, String>();
        toMap(xml, map);
        return map;
    }

    /**
     * xml 转换为 字典序 map
     *
     * @param xml
     * @return
     */
    public static Map<String, String> xmlToMap(String xml) {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        toMap(xml, map);
        return map;
    }

    private static void toMap(String xml, Map<String, String> map) {
        try {
            Document doc = DocumentHelper.parseText(xml);
            Element root = doc.getRootElement();
            for (Iterator<?> iterator = root.elementIterator(); iterator.hasNext(); ) {
                Element e = (Element) iterator.next();
                map.put(e.getName(), e.getText());
            }
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对象转为xml字符串
     *
     * @param o
     * @return
     */
    public static String objectToXml(Object o) {
        return mapToXml(objectToSortedMap(o));
    }

    /**
     * 对象转为xml字符串
     *
     * @param o
     * @return
     */
    public static String objectToXmlWithCDATA(Object o) {
        return mapToXmlWithCDATA(objectToSortedMap(o));
    }

    /**
     * xml 转 object
     *
     * @param xml
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T xmlToObject(String xml, Class<T> clazz) {
        SortedMap<String, String> map = XmlToSortedMap(xml);
        BeanWrapper wrapper = new BeanWrapperImpl(clazz);
        PropertyValues pvs = new MutablePropertyValues(map);
        wrapper.setPropertyValues(pvs, true);
        return (T) wrapper.getWrappedInstance();
    }

    /**
     * 从对象转换为字典序的map
     *
     * @param o
     * @return
     */
    private static SortedMap<String, String> objectToSortedMap(Object o) {
        SortedMap<String, String> map = new TreeMap<String, String>();
        Field[] fields = o.getClass().getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            try {
                if (f.get(o) != null && f.get(o) != "") {
                    map.put(f.getName(), f.get(o).toString());
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return map;
    }

    /**
     * 判断字符串是不是纯数字组成的
     *
     * @param str
     * @return
     */
    private static boolean isNumeric(String str) {
        return str != null && str.matches("\\d *");
    }

    /**
     * 创建普通 xml element
     *
     * @param key
     * @param value
     * @return
     */
    private static String createElement(String key, String value) {
        return "<" + key + ">" + value + "</" + key + ">";
    }

    /**
     * 创建 CDATA xml element
     *
     * @param key
     * @param value
     * @return
     */
    private static String createCDATAElement(String key, String value) {
        return "<" + key + "><![CDATA[" + value + "]]></" + key + ">";
    }

}
